﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.Threading;
using System.Windows;

namespace SimpleRayTracer
{
    class TracerMain
    {
        private List<ObjectContainer> objectList = new List<ObjectContainer>(); 

        private Graphics graphics;
        private int Height;
        private int Width;
        private int ImageHeight;
        private int ImageWidth;

        private Vector3D cameraUp = new Vector3D();
        private Vector3D cameraTarget = new Vector3D();
        private Vector3D viewerOrigin = new Vector3D();

        private Light light = new Light(new Vector3D(0, 0, 0));

        public double tracerProgress = 0;
        public double statusFOV = 0;

        public Bitmap InitTracer(int Width, int Height, int ImageWidth, int ImageHeight)
        {
            this.Width = Width;
            this.Height = Height;
            this.ImageWidth = ImageWidth;
            this.ImageHeight = ImageHeight;

            Bitmap bitmap = new Bitmap(ImageWidth, ImageHeight, PixelFormat.Format32bppArgb);
            Graphics g = Graphics.FromImage(bitmap);

            Color backgroundColour = Color.Black;
            g.FillRectangle(new SolidBrush(backgroundColour), new Rectangle(0, 0, ImageWidth, ImageHeight));
            Rectangle rect = new Rectangle(0, 0, ImageWidth, ImageHeight);
            
            graphics = g;
            return bitmap;
        }

        public void StartTracer(double rsX, double rsY, double rsZ,
                    double rdX, double rdY, double rdZ,
                    double vX, double vY, double vZ,
                    List<ObjectContainer> objectList,
                    double lcX, double lcY, double lcZ)
        {
            this.objectList = objectList;

            this.cameraUp.x = rsX;
            this.cameraUp.y = rsY;
            this.cameraUp.z = rsZ;

            this.cameraTarget.x = rdX;
            this.cameraTarget.y = rdY;
            this.cameraTarget.z = rdZ;

            this.viewerOrigin.x = vX;
            this.viewerOrigin.y = vY;
            this.viewerOrigin.z = vZ;

            this.light.centre = new Vector3D(lcX, lcY, lcZ);

            Thread tracerThrd = new Thread(Trace); // start ray tracing in seperate thread
            tracerThrd.Start();
        }

        public void Trace()
        {
            Vector3D normToColour = new Vector3D();

            Vector3D direction = new Vector3D();
            Camera orthoCam = new Camera(cameraUp, viewerOrigin, cameraTarget, ImageWidth, ImageHeight); 

            for (int row = 0; row <= ImageHeight; row++) // scan every line on screen
            {
                for (int col = 0; col <= ImageWidth; col++) // scan every column on screen
                {
                    bool isShadow = false;
                    bool isHit = false;
                    Object3D objectCheck = null;
                    Object3D objectHit = null;
                    double t = 1.0E10;
                    double collRes = -1.0;
                    double cost = 1.0;

                    Ray ray = orthoCam.getRayFromCamera(col, row, 0, 1.0E10);

                    for (int objIndex = 0; objIndex < objectList.Count; objIndex++)
                    {
                        objectCheck = objectList[objIndex].Object;

                        isHit = objectCheck.GetIntersect(ray, ref  collRes); // check for collision between ray and Object3D

                        if (collRes < 0)
                        {
                            continue;
                        }
                        else if ((collRes > 0) && (collRes < t))
                        {
                            t = collRes;
                            objectHit = objectCheck;

                            Vector3D pi = ray.origin + ray.direction * t;
                            cost = light.GetColourDiff(pi, objectHit.GetNormal(pi));
                            normToColour = objectHit.GetNormal(pi);
                        }

                    }

                    Color color = Color.FromArgb(0, 0, 0);
                    
                    //cost = Math.Abs(cost);

                    if (objectHit != null) // if a ray hit an Object3D start building the image
                    {
                        double newR = cost * objectHit.clR;
                        //double newR = Math.Abs(normToColour.x * 255);
                        if (newR >= 255) newR = 255;
                        double newG = cost * objectHit.clG;
                        //double newG = Math.Abs(normToColour.y * 255);
                        if (newG >= 255) newG = 255;
                        double newB = cost * objectHit.clB;
                        //double newB = Math.Abs(normToColour.z * 255);
                        if (newB >= 255) newB = 255;

                        color = Color.FromArgb((int)newR, (int)newG, (int)newB);
                    }

                    Brush brush = new SolidBrush(color);
                    graphics.FillRectangle(brush, col, row, 1, 1);
                    brush.Dispose();


                }
                tracerProgress = ((row * 100) / Height); // compute progress in percantage
                statusFOV = orthoCam.getFOV(); // get the field of view
            }
        }

        private void WriteFile(string outputText)
        {
            string fileName = "C:\\temp\\ImageOutput.csv";

            System.IO.StreamWriter streamWriter;
            streamWriter = new System.IO.StreamWriter(fileName);

            streamWriter.Write(outputText);
            streamWriter.Close();
        }

    }
}
